#!/usr/bin/env python
# -*- coding: utf-8 -*-
import threading
import webbrowser
from tkinter import *
from tkinter import ttk
from tkinter.messagebox import showinfo

from ttkbootstrap import Style


class Ui_Model:
    def __init__(self):
        self.window_style = Style(theme='minty')
        self.root = self.window_style.master
        self.reload()

    def reload(self):
        self.root.geometry("+400+20")  # +x+y：窗口偏移量，自定义窗口默认出现的位置
        self.root.title("Picture_Sniffing")
        self.style = ttk.Style()

        # 在大窗口下定义一个顶级菜单实例
        self.top_menu = Menu(self.root)
        # 在顶级菜单实例下创建子菜单实例
        theme_menu = Menu(self.top_menu)
        light_theme = Menu(theme_menu)
        dark_theme = Menu(theme_menu)
        for each in ['cosmo', 'flatly', 'litera', 'minty', 'lumen', 'sandstone',
                     'yeti', 'pulse', 'united', 'morph', 'journal', ]:
            light_theme.add_command(label=each, command=lambda name=each: self.change_theme(name))
        for each in ['darkly', 'superhero', 'solar', 'cyborg', ]:
            dark_theme.add_command(label=each, command=lambda name=each: self.change_theme(name))
        theme_menu.add_cascade(label='light', menu=light_theme)
        theme_menu.add_cascade(label='dark', menu=dark_theme)
        # 为顶级菜单实例添加菜单，并级联相应的子菜单实例
        self.top_menu.add_cascade(label='开始', menu='')
        self.top_menu.add_cascade(label='设置', menu='')
        self.top_menu.add_cascade(label='主题', menu=theme_menu)
        self.top_menu.add_cascade(label='关于', menu='')
        self.root['menu'] = self.top_menu

        self.thread_available = True  # 子线程是否可用的信号量，目前来说设置一个子线程足矣

        self.frame_left_top = Frame(width=400, height=250)
        self.frame_right_top = Frame(width=400, height=250)
        self.frame_center = Frame(width=800, height=500)
        self.frame_bottom = Frame(width=800, height=10)

        # 定义左上方区域
        v = StringVar()
        v.set('no')
        self.selenium = False
        self.use_selenium_choose = Label(self.frame_left_top, text="启用无头浏览器:", font=('Arial', 18))
        self.use_selenium = Radiobutton(self.frame_left_top, text="是",
                                        variable=v, value='yes',
                                        command=lambda: self.selenium_choose('true'),
                                        font=('Arial', 18))
        self.no_selenium = Radiobutton(self.frame_left_top, text="否",
                                       variable=v, value='no',
                                       command=lambda: self.selenium_choose('false'),
                                       font=('Arial', 18))
        self.use_selenium_choose.grid(row=0, column=0, columnspan=2, padx=20, pady=20)
        self.use_selenium.grid(row=0, column=3, padx=5, pady=20)
        self.no_selenium.grid(row=0, column=4, padx=5, pady=20)

        self.left_top_title = Label(self.frame_left_top, text="资源状态:", font=('Arial', 25))
        self.left_top_title.grid(row=2, column=0, columnspan=2, sticky=NSEW, padx=30, pady=10)

        self.var_total = StringVar()  # 声明资源总数数
        self.var_downloaded = StringVar()  # 声明已下载资源总数
        self.img_list = []  # 图片资源列表
        self.img_downloaded_count = 0
        self.selected_item_value = []  # 被选中的条目的值列表
        self.allow_type = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tiff', 'ai', 'cdr', 'eps', ]  # 常见图片类型
        self.not_allowed_name = ['/', "\\", ':', '*', '?', '"', '<', '>', '|', ]  # 文件名非法字符

        self.left_top_frame = Frame(self.frame_left_top)
        self.left_top_frame_left1 = Label(self.frame_left_top, text="总资源数:", font=('Arial', 20))
        self.left_top_frame_left2 = Label(self.frame_left_top, textvariable=self.var_total, font=('Arial', 15))

        self.left_top_frame_right1 = Label(self.frame_left_top, text="已下载数:", font=('Arial', 20))
        self.left_top_frame_right2 = Label(self.frame_left_top, textvariable=self.var_downloaded, font=('Arial', 15))

        self.left_top_frame_left1.grid(row=3, column=0)
        self.left_top_frame_left2.grid(row=3, column=1)
        self.left_top_frame_right1.grid(row=4, column=0)
        self.left_top_frame_right2.grid(row=4, column=1)

        # 定义右上方区域
        self.var_url_entry = StringVar()
        # self.var_type_entry = StringVar()
        # TODO:可扩展为带输入的下拉框，就可以实现内置一些网站地址，同时支持用户输入的效果，但还没决定是否要这么做
        self.right_top_title = Label(self.frame_right_top, text="请输入目标网址：", font=('Arial', 20))
        self.right_top_entry = Entry(self.frame_right_top, textvariable=self.var_url_entry, width=50)

        self.right_top_img_type = Label(self.frame_right_top, text="输入网页图片格式(jpg/png/gif等)", font=('Arial', 18))
        self.right_top_img_type2 = Label(self.frame_right_top, text="不填默认为不区分后缀:", font=('Arial', 15))
        self.right_top_img_type_select = ttk.Combobox(self.frame_right_top)  # 设置为可输入的下拉框
        self.right_top_img_type_select['value'] = [''] + self.allow_type  # 选项列表
        self.right_top_img_type_select.current(0)  # 默认为''

        self.right_top_button_sniff = Button(self.frame_right_top, text="开始嗅探",
                                             command=lambda: self.thread_it(self.sniff_start),
                                             font=('Arial', 15))
        self.right_top_button_download = Button(self.frame_right_top, text="开始下载",
                                                command=lambda: self.thread_it(self.download_start),
                                                font=('Arial', 15))
        self.downloading_jud = False
        self.right_top_title.grid(row=2, column=0, columnspan=3, pady=15)
        # 使用columnspan来表示占的列数，防止默认占一列时单元格单位长变成该部件的长度
        self.right_top_entry.grid(row=3, column=0, columnspan=2, pady=10)
        self.right_top_img_type.grid(row=4, column=0, columnspan=2)
        self.right_top_img_type2.grid(row=5, column=0, pady=5)
        self.right_top_img_type_select.grid(row=5, column=1, pady=5)
        self.right_top_button_sniff.grid(row=6, column=0, pady=5)
        self.right_top_button_download.grid(row=6, column=1, pady=5)

        # 定义中心列表区域
        self.tree = ttk.Treeview(self.frame_center, show="headings", height=15, columns=("a", "b", "c", "d"))
        self.vbar = ttk.Scrollbar(self.frame_center, orient=VERTICAL, command=self.tree.yview)
        # 定义树形结构与滚动条
        self.tree.configure(yscrollcommand=self.vbar.set)
        self.style.configure('Treeview', rowheight=30)  # 调整Treeview行高

        # 表格的标题
        self.tree.column("a", width=75, anchor="center")  # 列居中
        self.tree.column("b", width=200, anchor="w")  # 设置列左对齐
        self.tree.column("c", width=400, anchor="w")  # 左对齐
        self.tree.column("d", width=100, anchor="center")
        self.tree.heading("a", text="编号")
        self.tree.heading("b", text="图片名称")
        self.tree.heading("c", text="图片链接")
        self.tree.heading("d", text="下载状态")

        # 表格项目右键菜单
        self.popup = Menu(self.root, tearoff=0)
        self.popup.add_command(label="查看原图", command=self.look_by_url)
        self.popup.add_command(label="删除该项", command=self.delete_select_item)
        self.popup.add_command(label="区间删除", command=self.delete_select_more_items)
        self.popup.add_command(label="停止下载", command=self.download_stop)
        self.popup.add_command(label="重置下载", command=self.download_reset)

        # 调用方法获取表格内容插入
        self.tree.grid(row=0, column=0, sticky=NSEW)
        self.vbar.grid(row=0, column=1, sticky=NS)

        # 整体区域定位
        self.frame_left_top.grid(row=0, column=0, padx=2, pady=5)
        self.frame_right_top.grid(row=0, column=1, padx=30, pady=5)
        self.frame_center.grid(row=1, column=0, columnspan=2, padx=4, pady=5)
        self.frame_bottom.grid(row=2, column=0, columnspan=2)

        self.frame_left_top.grid_propagate(0)
        self.frame_right_top.grid_propagate(0)
        self.frame_center.grid_propagate(0)
        self.frame_bottom.grid_propagate(0)

        self.tree.bind("<Button-1>", self.do_popup)  # 单击表项弹出菜单
        # TODO:可扩展功能 表格双击可编辑，暂时可能没什么用

        self.update_data()  # 界面统一更新

        self.root.mainloop()

    def update_data(self):
        self.get_total()  # 调用方法更新资源总数
        self.get_downloaded()  # 调用方法更新已下载数
        self.get_tree()  # 更新表格

    def change_theme(self, theme_name):
        self.root.destroy()
        self.window_style = Style(theme=theme_name)
        self.root = self.window_style.master
        self.reload()

    # 为了解决事件响应时tkinter界面卡住，采用多线程处理，所有耗时的函数都经过该方法单独开辟一个线程去执行
    # 所有的绑定事件均调用这个方法，使用lambda表达式传参,将要执行的目标函数作为其参数，格式统一又相互独立
    # 举例：self.popup.add_command(label="查看原图", command=lambda: self.thread_it(self.look_by_url))
    def thread_it(self, func, *args):
        """将函数打包进线程"""
        # 创建
        if self.thread_available:
            self.thread_available = False
            t = threading.Thread(target=func, args=args)
            # 守护 !!!
            t.setDaemon(True)
            # 启动
            t.start()
            # 阻塞--卡死界面！
            # t.join()

    def retry(self, messages):
        # 弹出对话框
        showinfo(title='通知', message=messages)

    # 下载状态改变：
    def download_stop(self):
        if self.downloading_jud:
            self.downloading_jud = False
            self.retry("已经为您停止下载，再次点击开始下载可继续下载，已下载过的不会重复下载哦！")
        else:
            self.retry("您还未开始下载哦！")

    # 重置下载状态
    def download_reset(self):
        if self.downloading_jud:
            self.downloading_jud = False
        if self.img_downloaded_count == 0:
            self.retry("您目前还下载任何资源哦！")
        elif self.img_downloaded_count > 0:
            for i in range(0, self.img_downloaded_count):
                alt, src, status = self.img_list[i]
                if status == 'success':
                    self.img_list[i] = (alt, src, 'false')
            while 1:  # 判断下载线程还未完全停止
                if self.thread_available:
                    alt, src, status = self.img_list[self.img_downloaded_count - 1]
                    if status == 'success':
                        self.img_list[self.img_downloaded_count - 1] = (alt, src, 'false')
                    break
            self.img_downloaded_count = 0
            self.retry("已为您重置下载状态！点击开始下载会重新开始哦！")

    def selenium_choose(self, choice):
        self.selenium = False if choice == 'false' else True

    # 得到资源总数
    def get_total(self):
        self.var_total.set(len(self.img_list))
        self.left_top_frame_left2.after(500, self.get_total)

    # 得到已下载数
    def get_downloaded(self):
        self.var_downloaded.set(self.img_downloaded_count)
        self.left_top_frame.after(500, self.get_downloaded)

    # 表格内容插入
    def get_tree(self):
        # 删除原节点
        for _ in map(self.tree.delete, self.tree.get_children("")):
            pass
        # 更新插入新节点
        for i in range(len(self.img_list)):
            self.tree.insert("", "end", values=(i + 1, *self.img_list[i]))
        self.tree.after(500, self.get_tree)

    # 开始嗅探
    def sniff_start(self):
        for i in range(20):
            self.img_list.append(("Submitted",
                                  "https://pic.3gbizhi.com/2021/0923/20210923092202834.jpg",
                                  "false"))
        self.thread_available = True

    def download_start(self):
        pass

    def do_popup(self, event):
        # 稍微偏离鼠标位置，多次实践证明，有效避免选择指定条目的命中率
        selected_item = self.tree.identify("item", event.x, event.y)  # 根据点击坐标获取行条目对象
        self.selected_item_value = self.tree.item(selected_item)['values']  # 获取选中条目的值列表

        # 动态设置弹出菜单的位置，稍微偏离鼠标所在位置，比较人性化，防止连点时误触到选项
        try:
            self.popup.tk_popup(event.x_root + 40, event.y_root + 10, 0)
        finally:
            self.popup.grab_release()

    # 点击查看原图选项
    def look_by_url(self):
        look_url = self.selected_item_value[2]  # 将选中行条目的url作为要查看的url
        webbrowser.open(look_url)  # 调用电脑默认浏览器打开选中条目的url

    # 点击删除选项
    def delete_select_item(self):
        selected_id = self.selected_item_value[0] - 1  # 根据每行的编号-1即可得到其实际ID(ID是从0开始计数的)
        del self.img_list[selected_id]

    def delete_select_more_items(self):
        # Create a Button to Open the Toplevel Window
        jump_window = Toplevel(self.root)
        jump_window.geometry("325x200+600+250")
        jump_window.title("请输入删除的行编号区间！")
        # Create a label in Toplevel window
        Label(jump_window, text="区间结束值必须大于起始值!").grid(row=1, column=2, padx=30, pady=10, columnspan=2)
        Label(jump_window, text="区间起始值：").grid(row=2, column=2, padx=30)
        Label(jump_window, text="区间结束值：").grid(row=4, column=2, padx=30)
        # 创建下拉菜单
        select_start = ttk.Combobox(jump_window, state='readonly')
        select_end = ttk.Combobox(jump_window, state='readonly')
        select_start.grid(row=2, column=3, padx=10, pady=20)
        select_end.grid(row=4, column=3, padx=10)
        # 设置下拉菜单中的值
        select_value = ['无']
        for i in range(1, len(self.img_list) + 1):
            select_value.append(i)
        select_start['value'] = select_value
        select_end['value'] = select_value
        select_start.current(0)
        select_end.current(0)
        # 设置选项
        confirm_btn = Button(jump_window, text="确认删除",
                             command=lambda: self.delete_more_items_confirm(jump_window, select_start, select_end),
                             font=('Arial', 15))
        cancel_btn = Button(jump_window, text="取消操作",
                            command=jump_window.destroy,
                            font=('Arial', 15))
        confirm_btn.grid(row=5, column=2, padx=10, pady=10)
        cancel_btn.grid(row=5, column=3, padx=10, pady=10)

    def delete_more_items_confirm(self, jump_window, select_start, select_end):
        start = select_start.get()
        end = select_end.get()
        jump_window.destroy()
        if start != '无' and end != '无':
            start, end = int(start) - 1, int(end)
            if end >= start:
                del self.img_list[start:end]
                self.retry(f"已成功为您删除所选的{end - start}项！")
            else:
                self.retry("区间结束值必须大于等于开始值！！！")


if __name__ == '__main__':
    window_object = Ui_Model()
